package org.activequant.dao.hibernate;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.marketscanner.InstrumentSpecificationDetails;
import org.activequant.dao.ISpecificationDetailsDao;
import org.activequant.util.exceptions.DaoException;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;


/**
* SpecificationDetailsDao extends DaoSupportBase&lt;InstrumentSpecificationDetails&gt; implements ISpecificationDetailsDao.<br/>
* Holds the following inherited variables:
* <ul>
* <li>clazz(Class&lt;InstrumentSpecificationDetails&gt;)</li>
* <li>hibernateTemplate(HibernateTemplate)</li>
* </ul>
* <br>
* <b>History:</b><br>
*  - [25.08.2007] Created (Erik Nijkamp)<br>
*  - [18.10.2007] Fixing incremental update (Ulrich Staudinger)<br> 
*  
*  @author Erik Nijkamp
*  @author Ulrich Staudinger
*/
public class SpecificationDetailsDao extends DaoSupportBase<InstrumentSpecificationDetails> implements ISpecificationDetailsDao {
	
	Logger log = Logger.getLogger(SpecificationDetailsDao.class);
	/**
	 * Constructs a SpecificationDetailsDao(extends DaoSupportBase&lt;InstrumentSpecificationDetails&gt; implements ISpecificationDetailsDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>InstrumentSpecificationDetails.class</code>.
	 */
	public SpecificationDetailsDao() {
		super(InstrumentSpecificationDetails.class);
	}
	/**
	 * Constructs a SpecificationDetailsDao(extends DaoSupportBase&lt;InstrumentSpecificationDetails&gt; implements ISpecificationDetailsDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>InstrumentSpecificationDetails.class</code>.<br/>
	 * The given sessionFactory(SessionFactory) is used to set its Hibernate SessionFactory and create/initialize the inherited hibernateTemplate(HibernateTemplate) for it.
	 * @param sessionFactory
	 */
	public SpecificationDetailsDao(SessionFactory sessionFactory) {
		super(InstrumentSpecificationDetails.class, sessionFactory);
	}
	/**
	 * <strong>1.</strong> Deletes the given entities(List&lt;InstrumentSpecificationDetails&gt;) instances themselves.
	 */
	@Override
	public void delete(List<InstrumentSpecificationDetails> entities) throws DaoException {
		try {		
			//HqlQueries.deleteSpecificationEntities(getHibernateTemplate(), entities.toArray(new InstrumentSpecificationDetails[] {}));
			getHibernateTemplate().deleteAll(new ArrayList<InstrumentSpecificationDetails>(entities));
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}   	
	}
	/**
	 * <strong>1.</strong> Deletes all InstrumentSpecificationDetails instances.
	 */
	@Override 
	@SuppressWarnings("unchecked")
	public void deleteAll() {
		try {
			List<InstrumentSpecificationDetails> specs = getHibernateTemplate().loadAll(clazz);
			// hibernate does not support orphan-delete on one-to-one or many-to-one
			//HqlQueries.deleteSpecificationEntities(getHibernateTemplate(), specs.toArray(new InstrumentSpecificationDetails[] {}));
			getHibernateTemplate().deleteAll(specs);
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}
	}
	/**
	 * Saves/updates and returns the given entity(InstrumentSpecificationDetails).<br/>
	 * If there was no id, it looks it up by finding an example.
	 */
	public InstrumentSpecificationDetails findByInstrumentSpecification(InstrumentSpecification entity) {
		if (entity.hasId()) {
			long id = entity.getId();
			InstrumentSpecificationDetails storedSpec = super.find(id);
			if (storedSpec != null && storedSpec.getTimeStamp().getDate().after(calendar.getTime())) {
				return storedSpec;
			}			
			//TODO: look up instrumentSpec by example if no ID
		}
		InstrumentSpecification existingSpec = lookupIfNeeded(entity);
		if(existingSpec!=null){
			InstrumentSpecificationDetails storedSpec = super.find(existingSpec.getId());
			return storedSpec;
		}
		return null;
	}
	/**
	 * Saves/updates and returns the given entity(InstrumentSpecificationDetails).<br/>
	 * If there was no id, it looks it up by finding an example.
	 */
	@Override
	public InstrumentSpecificationDetails update(InstrumentSpecificationDetails entity) {
		Long id=null;
		if(entity.getInstrumentSpecification().hasId()){
			id=entity.getInstrumentSpecification().getId();
		}else{
			InstrumentSpecification spec = entity.getInstrumentSpecification();
			InstrumentSpecification existingSpec = lookupIfNeeded(spec);
			if(existingSpec!=null&&existingSpec.hasId()){
				id=existingSpec.getId();
				spec.setId(id);
			}else{
				getHibernateTemplate().saveOrUpdate(spec);
				id=spec.getId();
			}
		}
		InstrumentSpecificationDetails storedSpec = super.find(id);
		if(storedSpec!=null&&storedSpec.getTimeStamp().getDate().after(calendar.getTime())){
			entity.setId(id);
			return storedSpec;
		}else{
			return super.update(entity);
		}
	}

	
	static Calendar calendar=Calendar.getInstance();
	static{
		calendar.add(Calendar.MONTH, -1);
	}
	
	/**
	 * <strong>1.</strong> If the given spec(InstrumentSpecification) has a non-null id(Long), it just returns the given spec(InstrumentSpecification) without doing anything else.<br/>
	 * <strong>2.</strong> Otherwise, it uses the given spec(InstrumentSpecification) as an example to find and return the InstrumentSpecification.<br/> 
	 * (it must find exactly one, more than one is considered ambiguous and throws an exception)
	 * @param spec
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private InstrumentSpecification lookupIfNeeded(final InstrumentSpecification spec) {
		if(spec.hasId()) return spec; // not needed
		
		return (InstrumentSpecification)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				String queryString=SpecificationDao.createInstrumentSpecificationLookupQuery(spec);
				Query query = session.createQuery(queryString);
				List<InstrumentSpecification> list= query.list();
				if(list.size()==0){
					//log.warn("No instrument specification for example found ...");
					return null; 
				}else if(list.size() > 1) {
					throw new IllegalArgumentException("ambiguity: instrumentSpecification matches more than one database entity: " + spec);
				}
				return list.get(0);
			}
		});
	}
}

